package hu.mapro.magicroller.core;

import hu.mapro.gamelib.SectionedLinearLevel;
import hu.mapro.gamelib.SessionScreen;
import hu.mapro.gamelib.SideScrollerSession;

import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public final class MagicRollerSession extends SideScrollerSession {

	public static class Container {
	
		final World world;
		final MagicRollerPlayer player;
		final MagicRollerLevelGenerator generator;
		final MagicRollerConstants constants;
		final OrthographicCamera camera;
		final SectionedLinearLevel level;
		
		public Container() {
			constants = new MagicRollerConstants();
			world = new World(new Vector2(0, -9.8f), false);
			camera = new OrthographicCamera();
			player = new MagicRollerPlayer(constants, world);
			generator = new MagicRollerLevelGenerator(constants, world, camera);
			level = new SectionedLinearLevel(
					constants.BOTTOM_Y, 
					Float.MAX_VALUE, 
					new MagicRollerLevelGenerator(constants, world, camera),
					SectionedLinearLevel.NULL_REAR_BLOCKER, 
					constants.VIEW_WIDTH, 
					constants.REAR_VISIBILITY, 
					constants.RETREAT,
					constants.CAR_POSITION.x
			);
		}
		
		void dispose() {
			level.dispose();
			generator.dispose();
			player.dispose();
			world.dispose();
		}
		
	}
	
	final Container container;
	final SessionScreen sessionScreen;
	
	public MagicRollerSession(
			Container container,
			SessionScreen sessionScreen
	) {
		super(
				container.camera,
				container.world,
				container.level,
				container.player,
				Direction.RIGHT,
				container.constants.VIEW_WIDTH,
				container.constants.MARGIN
		);
		this.container = container;
		this.sessionScreen = sessionScreen;
		
		rampBodyDef.type = BodyType.DynamicBody;
		rampShape.set(new float[] {
				0, 0,
				0, container.constants.MAX_UP,
				-container.constants.MAX_UP*2, 0
		});
		rampFixtureDef.shape = rampShape;
		rampFixtureDef.friction = container.constants.RAMP_FRICTION;
		rampFixtureDef.density = container.constants.RAMP_DENSITY;
		
		
		floorBodyDef.type = BodyType.DynamicBody;
		floorFixtureDef.shape = floorShape;
		floorFixtureDef.friction = container.constants.FLOOR_FRICTION;
		floorFixtureDef.density = container.constants.FLOOR_DENSITY;
		
		shapeRenderer = new ShapeRenderer();
	}

	final BodyDef rampBodyDef = new BodyDef();
	final PolygonShape rampShape = new PolygonShape();
	final FixtureDef rampFixtureDef = new FixtureDef();
	
	final BodyDef floorBodyDef = new BodyDef();
	final PolygonShape floorShape = new PolygonShape();
	final float[] floorVertices = new float[8];
	final FixtureDef floorFixtureDef = new FixtureDef();
	
	final ShapeRenderer shapeRenderer;
	
	
	
	@Override
	public void dispose() {
		shapeRenderer.dispose();
		container.dispose();
		rampShape.dispose();
		super.dispose();
	}
	
	InputProcessor inputProcessor = new InputAdapter() {
		
		@Override
		public boolean touchDown(int screenX, int screenY, int pointer,	int button) {
//			Vector3 vec = new Vector3(screenX, screenY, 0);
//			camera.unproject(vec);
//			rampBodyDef.position.set(vec.x, vec.y);
//			
//			Body rampBody = world.createBody(rampBodyDef);
//			rampBody.createFixture(rampFixtureDef);

			startCasting(screenX, screenY);
			
			return false;
		}
		
		public boolean touchDragged(int screenX, int screenY, int pointer) {
			keepCasting(screenX, screenY);
			
			return false;
		};
		
		public boolean touchUp(int screenX, int screenY, int pointer, int button) {
			finishCasting(screenX, screenY);
			
			return false;
		}
		
		
		
		public boolean keyDown(int keycode) {
			switch (keycode) {
			case Input.Keys.ESCAPE:
			case Input.Keys.BACK:
				sessionScreen.pause();
				break;
			case Input.Keys.R:
				sessionScreen.runningRestart();
				break;
			case Input.Keys.X:
				sessionScreen.menuExit();
				break;
			}
			return false;
		};
		
	};

	public void play() {
		super.play();
		
		setupInput();
	}
	
	@Override
	public void pause() {
		super.pause();
		
		sessionScreen.inputs.removeProcessor(inputProcessor);
		cancelCasting();
	}
	
	private void setupInput() {
		sessionScreen.inputs.addProcessor(inputProcessor);
	}

	boolean isCasting = false;
	Vector2 castingStart = new Vector2();
	int castingX;
	int castingY;
	
	final Vector3 unprojectVector = new Vector3(0,0,0);
	
	void startCasting(int screenX, int screenY) {
		isCasting = true;
		screenToWorld(screenX, screenY);
		castingStart.x = unprojectVector.x;
		castingStart.y = unprojectVector.y;
		saveCastingEnd(screenX, screenY);
	}

	private void screenToWorld(int screenX, int screenY) {
		unprojectVector.x = screenX;
		unprojectVector.y = screenY;
		camera.unproject(unprojectVector);
	}

	void keepCasting(int screenX, int screenY) {
		if (isCasting) {
			saveCastingEnd(screenX, screenY);
		}
	}

	private void saveCastingEnd(int screenX, int screenY) {
		castingX = screenX;
		castingY = screenY;
	}
	
	void finishCasting(int screenX, int screenY) {
		if (isCasting) {
			isCasting = false;
			
			screenToWorld(screenX, screenY);
			
			float dx = unprojectVector.x - castingStart.x; 
			float dy = unprojectVector.y - castingStart.y;
			float len = (float) Math.sqrt(dx*dx + dy*dy);
			
			if (len >= container.constants.FLOOR_MIN_LEN) {
				
				float dx0 = (dx/len)*container.constants.FLOOR_THICKNESS;
				float dy0 = (dy/len)*container.constants.FLOOR_THICKNESS;
				
				// p1
				floorVertices[0] = castingStart.x + dy0;  
				floorVertices[1] = castingStart.y - dx0;
				// p2
				floorVertices[2] = unprojectVector.x + dy0;  
				floorVertices[3] = unprojectVector.y - dx0;
				// p3
				floorVertices[4] = unprojectVector.x - dy0;  
				floorVertices[5] = unprojectVector.y + dx0;
				// p4
				floorVertices[6] = castingStart.x - dy0;  
				floorVertices[7] = castingStart.y + dx0;  
				
				floorShape.set(floorVertices);
				Body floorBody = world.createBody(floorBodyDef);
				floorBody.createFixture(floorFixtureDef);
				
			}
			
			
		}
	}
	
	void cancelCasting() {
		isCasting = false;
	}
	
	@Override
	protected void drawGame() {
		super.drawGame();
		
		if (isCasting) {
			screenToWorld(castingX, castingY);
			
			shapeRenderer.setProjectionMatrix(camera.combined);
			shapeRenderer.begin(ShapeType.Line);
			shapeRenderer.setColor(Color.RED);
			shapeRenderer.line(castingStart.x, castingStart.y, unprojectVector.x, unprojectVector.y);
			shapeRenderer.end();
		}
	}
}
